package models;
import java.net.Socket;
import java.util.Arrays;
import java.util.Random;
 
import javax.swing.JOptionPane;
 
 
public class Game
{
        private Player[] players;
        private int plNr;
        private Deck deck ;
        private int startCoins;
        private int whoDealer;
        private int whoseTurn;
        private int playerWithSmallBlind;
        private int stake = 0;
        private int round = 1;
        private int smallBlind;
        private int bigBlind;
        private int maxBet;
        private int moveCounter = 1; // liczba wykonanych ruchow w rundzie
        private int playersExchangedCardsCounter  = 0;
        private int playersFoldedCounter = 0;
        private int playersInGameCounter;
        private int playersBeingInGameInActualRound;
        private boolean isBet = false;
        private ManagerOfClients moc;
        private boolean ending;
        /**
        * Constructor of Game
        *   @param moc ManagerOfClients
        *   @param deck A deck with a cards
        */
        public Game(ManagerOfClients moc, Deck deck)
        {
        	this.moc =moc;

        	this.startCoins = moc.getStartingCoins();
        	this.smallBlind = moc.getSmallBlind();
        	this.bigBlind = moc.getBigBlind();
            this.deck = deck;
        }
         /*
        *  Initializator of start Game
        *
        */
        public void startGame(){
 
        	ending = false;
        	this.players = moc.getClients();
        	this.plNr = moc.numberOfPlayers();
            this.playersInGameCounter = plNr;
            this.playersBeingInGameInActualRound = plNr;
            round = 1;
            stake = 0;
            playersExchangedCardsCounter  = 0;
            playersFoldedCounter = 0;
                whoDealer = Croupier.drawDealer(plNr);
                System.out.println("whoDealer:"+whoDealer);
                setPlayersWithBlinds();
                System.out.println(players.length);
                whoseTurn = Croupier.whichPlayerNext(whoseTurn, plNr);
                for(int i=0; i<players.length; i++)
                	if(players[i] != null)
                        players[i].displayCards();
                System.out.println(whoseTurn+"whoseturn");
                enableButtons(players[whoseTurn], isBet);
 
        }
        /**
        * Adding players to Game
        *   @return this.plNr A number of players
        */
        public int getPlayersInGame()
        {
        	return this.plNr;
        }
         /**
        * Assigns small and big Blind to players
        *
        */
        public void setPlayersWithBlinds(){
                playerWithSmallBlind = Croupier.whichPlayerNext2(whoDealer, players,false);
                while(players[playerWithSmallBlind].getPlayersCoins()<smallBlind){
                        updateFold(playerWithSmallBlind);
                        playerWithSmallBlind = Croupier.whichPlayerNext2(whoDealer, players,false);
                }
                playerWithSmallBlind = Croupier.whichPlayerNext2(whoDealer, players,false);
                players[playerWithSmallBlind].setBet(smallBlind);
                displaySmallBlind(players[playerWithSmallBlind].getNumber(),smallBlind);
                updateCoins(players[playerWithSmallBlind],0-smallBlind);
                int playerWithBigBlind = Croupier.whichPlayerNext2(playerWithSmallBlind, players, false);
               
                	while(players[playerWithBigBlind].getPlayersCoins()<bigBlind){
                        updateFold(playerWithBigBlind);
                        playerWithBigBlind = Croupier.whichPlayerNext2(playerWithBigBlind, players,false);
                	}
                 
                
                
                players[playerWithBigBlind].setBet(bigBlind);
                maxBet=bigBlind;
                whoseTurn = playerWithBigBlind;
                displayBigBlind(players[playerWithBigBlind].getNumber(),bigBlind);
                updateCoins(players[playerWithBigBlind],0-bigBlind);
                updateStake(smallBlind+bigBlind);
        }
        /**
        * @param amount Amount of coins
        * Display Coins of a players
        */
        public void displayCoins(int amount, int Id){
                String am = Integer.toString(amount);
                for(int i=0; i<players.length; i++){
                        players[i].displayCoins(am, Id);
                }
        }
        /**
        * A method which gets Winners
        * @return winners A player who have won
        *
        */
    private String getWinners() {

        String winners = "";
        int[][] Hands = new int[players.length][];
 
        for(int i = 0; i < players.length; i++) {
            Hands[i] = Hand.getReducedHand(players[i].getPlayerHand().getCards());
        }
        int[] bestHand = {-1,-1,-1,-1};
        for(int i = 0; i < players.length; i++) {
            if(players[i].getStatus() != Player.PLAYER_OUT_OF_GAME) {
                if(bestHand[0] != -1)
                    bestHand = Hand.giveBetterHand(bestHand, Hands[i]);
                else
                    bestHand = Hands[i];
            }
        }
        for(int i = 0; i < players.length; i++) {
            if(players[i].getStatus() == Player.PLAYER_OUT_OF_GAME){
                winners += "L";
            }
            else {
                if (!(bestHand.equals(Hands[i]))) {
                    if (!(Hand.giveBetterHand(Hands[i], bestHand).equals(Hand.giveBetterHand(bestHand, Hands[i])))) {
                        winners += "W";
                    } else
                        winners += "L";
                } else
                    winners += "W";
            }
        }
        System.out.println(winners);
        return winners;
 
    }
    /*
    * Displaying Winner of current game
    */
    public void displayWinner() {
        String message = "";
        for(int i = 0; i < players.length; i++) {
            if(players[i].getStatus() == Player.PLAYER_OUT_OF_GAME) {
                message += players[i].getNumber() + "FOLDED**"; //must be 8 characters
            }
            else {
                message += players[i].getNumber() + players[i].getPlayerHand().getString();
            }
        }
        message += getWinners();
        for(int i=0; i<players.length; i++){
            players[i].displayWinner(message);
        }
        System.out.println("displayeWinner:" + message);
    }
    /*
    * Assign Prize to their Winner
    */
    public void givePlayerTheirPrize() {
        String winnersString = getWinners();
        int numberOfWinners = 0;
        for(int i = 0; i < winnersString.length(); i++) {
            if(Character.toString(winnersString.charAt(i)).equals("W")) {
                numberOfWinners++;
            }
        }
        if (numberOfWinners == 1) { //Only one winner. Bank doesn't take a prize
            for(int i = 0; i < winnersString.length(); i++) {
                if(Character.toString(winnersString.charAt(i)).equals("W")) {
                    if(players[i].getStatus() != Player.PLAYER_PLAYED_ALLIN) { //regular winner (NOT ALL IN)
                        updateCoins(players[i], stake);
                    }
                    else { //player[i] played ALL IN
                        int prize = 0;
                        int allInBet = players[i].getBet();
                        for (int j = 1; j < players.length; j++) {
                            if(allInBet <= players[i].getBet())
                                prize += allInBet;
                            else
                                prize += players[i].getBet();
                        }
                        updateCoins(players[i], prize);
                    }
 
                }
            }
        }
 
    }
    /**
    * Update amount of coins
    * @param player Player 
    * @param change Change of amount
    */
        public void updateCoins(Player player, int change){
                int newCoins = player.getPlayersCoins() + change;
                player.setPlayersCoins(newCoins);
                int Id = player.getNumber();
                String s = Integer.toString(newCoins);
                for(int i=0; i<players.length; i++){
                	if( players[i]!=null)                		
                        players[i].displayCoins(s, Id);
                }
        }
        /**
        * @param playerNr An index of a player
        * @param bet A bet of current player
        */
        public void updateBet(int playerNr ,String bet){
                isBet = true;
                int temp = Integer.parseInt(bet);
                int sum = temp + players[playerNr].getBet();
                if (sum > maxBet) maxBet = sum;
                String s = Integer.toString(sum);
                players[playerNr].setBet(sum);
                updateCoins(players[playerNr],0-temp);
                updateStake(temp);
                for(int i=0; i<players.length; i++){
                	if(players[i]!=null)
                                players[i].displayBet(s, playerNr);
                }
                nextPlayerMove();
        }
        public void displayPlayersHands() {
        String[] playerHands = new String[6];
 
    }
    /**
    * @param bBlind BigBLind
    */
        public void displayBigBlind(int Id, int bBlind){
                String bB = Integer.toString(bBlind);
                for(int i=0; i<players.length; i++){
                				if(players[i] != null)
                                players[i].displayBigBlind(Id, bB);
                }
        }
         /**
        * @param sBlind SmallBlind
        */
        public void displaySmallBlind(int Id, int sBlind){
                String sB = Integer.toString(sBlind);
                for(int i=0; i<players.length; i++){
                	if( players[i]!=null)
                                players[i].displaySmallBlind(Id, sB);
                }
        }
        /**
        * @param status Current status of a player
        */
        public void displayNotes(int Id,int status){
                if(status==Player.PLAYER_PLAYED_ALLIN){
                        for(int i=0; i<players.length; i++){
                                players[i].displayAllin(Id);
                        }
                }
                else{
                        for(int i=0; i<players.length; i++){
                                players[i].displayFold(Id);
                        }
                }
        }
        /**
        * @param betChange A change of player's bet
        */
        public void updateStake(int betChange){
                stake = stake + betChange;
                for(int i=0; i<players.length; i++){
                	if(players[i] != null )
                        players[i].displayStake(Integer.toString(stake));
                }
               
        }
        /*
        * Updating round after round
        */
        public void updateRound(){
        	System.out.println("TEST");
            	System.out.println(playersExchangedCardsCounter);
            	System.out.println(plNr);
            	System.out.println(playersFoldedCounter);
            	int all_in = 0;
            	for(int i = 0; i<plNr; i++)
        	{
        		if(players[i].getStatus() == Player.PLAYER_PLAYED_ALLIN)
        			all_in++;
        	}
        	if(playersInGameCounter - all_in  <=1)
            {
        		round = 4;
            	//this.displayWinner();
            	//this.givePlayerTheirPrize();
            	//this.updateRozgrywka();
            }
        	else{
        		round++;
                moveCounter = 0;
                isBet = false;
                playersExchangedCardsCounter = 0;
                playersBeingInGameInActualRound = playersInGameCounter;
                whoseTurn = playerWithSmallBlind;
                for(int i=0; i<players.length; i++){
                        players[i].displayRound(Integer.toString(round));
                }
            	
        	}

        }
       /*
       * Method which display nextPlayerMove
       */
        public void nextPlayerMove(){
                if( playersFoldedCounter<(plNr-1) ){
                        if(playersBeingInGameInActualRound>1){ // chociaz dwoch graczy nie dalo fold ani all-in
                                whoseTurn = Croupier.whichPlayerNext(whoseTurn, plNr);
                        //gdy wszyscy wymienili karty
                        if(playersExchangedCardsCounter==(plNr-playersFoldedCounter) ){
                                updateRound();
                        }
                        //runda pierwsza i nikt przed osob z duza ciemna nie przebic duzej ciemnej
                        if(moveCounter==plNr-1 && round==1 && Croupier.havePlayersTheSameBets(players)){
                                enableButtons(players[whoseTurn], isBet);
                            players[whoseTurn].enableButtons("H"); //dodatkowo moze dac check
                                moveCounter++;
                        }//POCZATEK WYMIANY wszyscy gracze wykonali mogli wykonac ruch i gracze w grze maja te same zaklady
                        else if (moveCounter>=playersBeingInGameInActualRound && Croupier.havePlayersTheSameBets(players)){
                                if(round<4){
                                        while(players[whoseTurn].getStatus()==Player.PLAYER_OUT_OF_GAME){
                                                whoseTurn = Croupier.whichPlayerNext(whoseTurn, plNr);
                                        }
                                        enableCardsExchange(players[whoseTurn]);
                                        playersExchangedCardsCounter++;
                                }
                                else{ //KONIEC GRY po 4 rundach- przynajmniej dwoch graczy nie dalo fold ani all-in
                        displayWinner();
                        givePlayerTheirPrize();
                        updateRozgrywka();
                                        System.out.println("Koniec pierwszego rozdania");      
                                }
                        }
                        else{ //Kontynuacja ruchow- rozne zaklady lub nie wszyscy mogli wykonac ruch
                                while(players[whoseTurn].getStatus()!=Player.PLAYER_IN_GAME){
                                        whoseTurn = Croupier.whichPlayerNext(whoseTurn, plNr);
                            }
                            enableButtons(players[whoseTurn], isBet);
                            moveCounter++;
                        }
                        }//jest tylko jeden gracz, ktory nie dal fold ani all-in, i chociaz jeden z all-in
                        else{//wszyscy wymienili karty

                                if(playersExchangedCardsCounter==(plNr-playersFoldedCounter) ){
                                	updateRound();
                                	
                                }
                                
                                else{//trwa wymiana kart
                                        if(round<4){
                                        while(players[whoseTurn].getStatus()==Player.PLAYER_OUT_OF_GAME)
                                                whoseTurn = Croupier.whichPlayerNext(whoseTurn, plNr);
                                        enableCardsExchange(players[whoseTurn]);
                                        playersExchangedCardsCounter++;
                                }
                                else{//KONIEC GRY
                        displayWinner();
                        givePlayerTheirPrize();
                        updateRozgrywka();
                                        System.out.println("Koniec pierwszego rozdania");    
                                }
                                }
                        }
                }
                else{//Koniec GRY zostal tylko jeden gracz w grze(zwyciezca), reszta da�a fold
            displayWinner();
            System.out.println("stake:" +stake);
            givePlayerTheirPrize();
            for(int i=0; i<players.length; i++){
                System.out.println(players[i].getNick()+ " ->" + players[i].getPlayersCoins());
            }
            updateRozgrywka();
                        System.out.println("Koniec pierwszego rozdania");
                }
        }
 
        public void updateAllin(int playerNr){
                players[playerNr].setStatus(Player.PLAYER_PLAYED_ALLIN);
                String s = Integer.toString(players[playerNr].getPlayersCoins());
                playersInGameCounter--;
                updateBet(playerNr,s);
                displayNotes(players[playerNr].getNumber(), Player.PLAYER_PLAYED_ALLIN);
               
        }
        public void updateFold(int playerNr){
                players[playerNr].setStatus(Player.PLAYER_OUT_OF_GAME);
                displayNotes(players[playerNr].getNumber(), Player.PLAYER_OUT_OF_GAME);
                playersInGameCounter--;
                playersFoldedCounter++;
                nextPlayerMove();
               
        }
        /*
         * Method enable  pressing the buttons appropriately to the player state
         * H-Check,B-Bet,R-Raise,C-Call,F-Fold,A-Allin,E-Exchange
         */
        public void enableButtons(Player player, boolean isBet){
                int bet = player.getBet();
                int coins = player.getPlayersCoins();
                String moves = Croupier.possibleMoves(bet, coins, isBet, maxBet, round);
                System.out.println("moves"+moves);
                System.out.println(player.getNick());
                if(moves!=null)
                player.enableButtons(moves);               
        }
        private void enableCardsExchange(Player player){
                player.enableButtons("E");
        }
        public void exchangeCards(int PlayerNr, String binaryCardSet){
                int cardSet = Integer.parseInt(binaryCardSet, 2);
                if(cardSet>0){
                        int exchangedCardsCounter = 0;
                        for(int i=0; i<Hand.HAND_SIZE; i++){
                                if(binaryCardSet.charAt(i)=='1')
                                        exchangedCardsCounter++;
                        }
                        players[PlayerNr].exchangeCards(deck, cardSet);
                        players[PlayerNr].displayCards();
                        for(int i = 1; i<players.length; i++){
                                if (i!=PlayerNr&&players[i]!=null) players[i].displayAmountOfExchanged(Integer.toString(exchangedCardsCounter)); //gracz wie jaki ma nick 
                        }
                }
        //displayWinner();
                nextPlayerMove();
        }
        public void updateRozgrywka(){
                int playersNotWorkingCounter = 0;
                for(int i=0; i<players.length; i++){
                	if(players[i]!=null)
                	{
                        players[i].getPlayerHand().discardAllCards(deck);
                        if(players[i].isWorking() && players[i].getPlayersCoins()>0){
                                players[i].setStatus(Player.PLAYER_IN_GAME);
                                players[i].setBet(0);
                                players[i].displayBet("0",i);
                                
                        }
                        else {
                        	players[i].setStatus(Player.PLAYER_OUT_OF_GAME);
                        		System.out.println(i);
                                playersNotWorkingCounter++;
                                
                        }
                	}
                }                
                if(playersNotWorkingCounter > 0)
                {
                	//moc.stopGame();
                	ending = true;
                	for(int i = 0; i<players.length; i++)
                	{
                		System.out.println(i);
                		if(players[i].getStatus() ==Player.PLAYER_OUT_OF_GAME)
                			players[i].send("OUT_");
                		
                		
                	}
                	
                	
                	
                }
                System.out.println("shuffle");
                deck.shuffle();
                System.out.println("po shuffle");

                if(!ending)
                {
                for(int i=0; i<players.length; i++){
                        System.out.println("isWorking" + i + "->" + players[i].isWorking());
                        if(players[i].isWorking() &&players[i].getPlayersCoins()>0){
                                players[i].getPlayerHand().drawAllCards(deck, i);
                        }

                }
                if( (plNr-playersNotWorkingCounter)<3){
                        JOptionPane.showMessageDialog(null, "All player has gone except you" ,
                                        "Error", JOptionPane.ERROR_MESSAGE);
                }
                else{
                        round = 0;
                        stake = 0;
                        playersFoldedCounter = playersNotWorkingCounter;
                        playersInGameCounter = plNr-playersNotWorkingCounter;
                        updateRound();
                        whoDealer = Croupier.whichPlayerNext2(whoDealer, players, false);
                        setPlayersWithBlinds();
                        playersBeingInGameInActualRound = playersInGameCounter;
                        whoseTurn = Croupier.whichPlayerNext2(whoseTurn, players,false);
                        for(int i=0; i<players.length; i++)
                                if(players[i].isWorking() &&players[i].getPlayersCoins()>0){
                                        players[i].displayCards();
                                }
                        moveCounter = 1;
                        enableButtons(players[whoseTurn], isBet);
                }
                }
                else
                	moc.stopGame();
               
        }
        public void deletePlayer(int n){
                

               if(  playersInGameCounter != 0)
               {
            	   playersInGameCounter--;
               }
               if(playersBeingInGameInActualRound != 0)
            	   playersBeingInGameInActualRound--;
               playersFoldedCounter++;
			
            	
                players[n].setStatus(Player.PLAYER_OUT_OF_GAME);
                if(n==this.whoseTurn&&!ending)
                	this.nextPlayerMove();
                ending = true;
               		
                
                
        }

}